/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>

#define NR_WP 32

typedef struct watchpoint {
  int NO;
  char expr[32];
  uint32_t value;
  struct watchpoint *next;

  /* TODO: Add more members if necessary */

} WP;

static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;
static int wp_num = 0;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i ++) {
    wp_pool[i].NO = i;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
  }

  head = NULL;
  free_ = wp_pool;
}

void new_wp(char *arg) {
  if (free_ == NULL) {
	panic("FreeList is empty!");
  }

  WP *wp = free_;
  WP *cur = head;

  /* 记录表达式的值 */
  if (arg == NULL) {
    /* no expression given */
	printf("No expression.\n");
	return;
  }
  else {
    bool success = true;
    word_t val = expr(arg, &success);
	if(success) {
	  wp->value = val;
	}
	else {
	  printf("Expression is wrong.\n");
	  return;
	}
  }

  free_ = free_->next;

  wp->NO = wp_num++;
  wp->next = NULL;
  strcpy(wp->expr, arg);

  if (cur == NULL) {
    head = wp;
  }
  else {
    while (cur->next != NULL) {
      cur = cur->next;
    }
    cur->next = wp;
  }

  printf("watchpoint %d: %s\n", wp->NO, arg);
}

void free_wp(int wp) {
  int flag = 0;
  WP *previous, *current;
  previous = current = head;

  while (current != NULL) {
    if (current->NO == wp) {
	  flag = 1;
	  break;
	}

	previous = current;
	current = current->next;
  }

  if (flag == 0) {
    printf("No breakpoint number %d\n", wp);
	return;
  }

  if (previous == current) {
    head = head->next;
  }
  else {
    previous->next = current->next;
  }

  current->next = free_;
  free_ = current;
}

void watchpoint_display() {
  if (head == NULL) {
    printf("No watchpoints\n");
  }
  else {
	printf("Num      What\n");
    WP *cur = head;
	while (cur != NULL) {
	  char str[3];
	  sprintf(str, "%d", cur->NO);

	  printf("%d", cur->NO);
	  for (int i = 0; i < 9-strlen(str); i ++) {
	    printf(" ");
	  }
	  printf("%s\n", cur->expr);

	  cur = cur->next;
	}
  }
}

bool trace_watchpoint() {
  WP *cur = head;
  word_t val;
  bool success;

  while (cur != NULL) {
	success = true;
    val = expr(cur->expr, &success);
	if (success && (val != cur->value)) {
	  printf("Old value = %#x\n", cur->value);
	  printf("New value = %#x\n", val);
	  cur->value = val;
	  return true;
	}
	cur = cur->next;
  }

  return false;
}
